package com.dddframework.web.api;

import cn.hutool.core.collection.CollUtil;
import com.dddframework.core.contract.BaseRepository;
import com.dddframework.core.contract.Model;
import com.dddframework.core.contract.Page;
import com.dddframework.core.contract.Query;
import com.dddframework.core.utils.BeanKit;
import com.dddframework.core.utils.BizAssert;
import com.dddframework.core.utils.MappingKit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 模型控制器，实现该控制器的Controller，自带CRUD方法
 */
public interface ModelController {

    @PostMapping("/{modelName}/page")
    default Page<Model> postPage(@PathVariable("modelName") String modelName, @RequestBody Map<String, Object> query) {
        return convertQuery(getModelClass(modelName), query).page();
    }

    @GetMapping("/{modelName}/page")
    default Page<Model> getPage(@PathVariable("modelName") String modelName, Map<String, Object> query) {
        return convertQuery(getModelClass(modelName), query).page();
    }

    @PostMapping("/{modelName}/list")
    default List<Model> postList(@PathVariable("modelName") String modelName, @RequestBody Map<String, Object> query) {
        return convertQuery(getModelClass(modelName), query).list();
    }


    @GetMapping("/{modelName}/list")
    default List<Model> getList(@PathVariable("modelName") String modelName, Map<String, Object> query) {
        return convertQuery(getModelClass(modelName), query).list();
    }

    @GetMapping("/{modelName}/detail")
    default Model detail(@PathVariable("modelName") String modelName, Map<String, Object> query) {
        return convertQuery(getModelClass(modelName), query).first();
    }

    @GetMapping("/{modelName}/detail/{id}")
    default Model detail(@PathVariable("modelName") String modelName, @PathVariable("id") Serializable id) {
        return BaseRepository.of(getModelClass(modelName)).get(id);
    }

    @PostMapping({"/{modelName}/save", "/{modelName}/create"})
    default Model save(@PathVariable("modelName") String modelName, @RequestBody Map<String, Object> query) {
        Model model = convertModel(getModelClass(modelName), query);
        model.save();
        return model;
    }

    @PostMapping("/{modelName}/saveBatch")
    default void saveBatch(@PathVariable("modelName") String modelName, @RequestBody List<Map<String, Object>> params) {
        Class<Model> modelClass = getModelClass(modelName);
        BaseRepository.of(modelClass).save(convertModels(modelClass, params));
    }

    @PostMapping({"/{modelName}/update", "/{modelName}/modify"})
    default void update(@PathVariable("modelName") String modelName, @RequestBody Map<String, Object> query) {
        convertModel(getModelClass(modelName), query).update();
    }

    @PostMapping({"/{modelName}/delete/{id}", "/{modelName}/remove/{id}"})
    default void delete(@PathVariable("modelName") String modelName, @PathVariable("id") Serializable id) {
        BaseRepository.of(getModelClass(modelName)).delete(id);
    }

    static Class<Model> getModelClass(String modelName) {
        Class<Model> modelClass = MappingKit.get("MODEL_NAME", modelName);
        BizAssert.notNull(modelClass, "Model: {} not found", modelName);
        return modelClass;
    }

    static Query convertQuery(Class<Model> modelClass, Map<String, Object> queryMap) {
        Class<Query> queryClass = MappingKit.get("MODEL_QUERY", modelClass);
        BizAssert.notNull(queryClass, "Query not found");
        return BeanKit.ofMap(queryMap, queryClass);
    }

    static Model convertModel(Class<Model> modelClass, Map<String, Object> modelMap) {
        return BeanKit.ofMap(modelMap, modelClass);
    }

    static List<Model> convertModels(Class<Model> modelClass, List<Map<String, Object>> modelMaps) {
        if (CollUtil.isNotEmpty(modelMaps)) {
            return modelMaps.stream().map(modelMap -> BeanKit.ofMap(modelMap, modelClass)).collect(Collectors.toList());
        }
        return null;
    }

}
